package algebra.op;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import utils.exception.CoreReasoningException;

import kb.DescriptionLogicsKB;

import algebra.visitors.AlgebraicOPEvaluator;

import entities.assertion.ConceptAssertion;
import entities.role.Role;

public abstract class AlgebraicOP {

	public abstract boolean isLeaf();

	public abstract boolean isPure();

	public abstract String toSerializedString();

	// by default, all queries have no KB to be used. Must explicitly set a KB
	// during plan generation.
	// also, PQuery always needs a KB; CQuery,ScanQuery, IntersectionQuery never
	// allows one to set a KB.
	public abstract DescriptionLogicsKB getPlanEvalKB();

	// make all OPs immutable: k-free counterparts are generated via the method
	// below
	// note that subqueries are not affected
	public abstract AlgebraicOP getKFreeVersion();

	public List<ConceptAssertion> acceptVisitor(AlgebraicOPEvaluator eval)
			throws CoreReasoningException {
		return eval.visit(this);
	}

	public JoinOP asIntersectionOP() {
		return (JoinOP) this;
	}

	public ConstantOP asConstantOP() {
		return (ConstantOP) this;
	}

	public PrimaryOP asPrimaryOP() {
		return (PrimaryOP) this;
	}

	public ProjectionOP asProjectionOP() {
		return (ProjectionOP) this;
	}

	public ScanOP asScanOP() {
		return (ScanOP) this;
	}

	public SelectionOP asSelectionOP() {
		return (SelectionOP) this;
	}

	// extract all the features in C and Pd
	public abstract Set<Role> extractFeatures();

	public abstract Set<String> extractConstants();

	public boolean hasOnlyScanOP() {
		if (this instanceof ScanOP) {
			return true;
		} else if (this instanceof JoinOP) {
			JoinOP ip = this.asIntersectionOP();
			boolean all = true;
			all &= ip.getFirstSubQuery().hasOnlyScanOP();
			if (!all) {
				return false;
			}
			all &= ip.getSecondSubQuery().hasOnlyScanOP();
			if (!all) {
				return false;
			}
			return true;
		}
		return false;
	}

	public Set<ScanOP> getAllScanOP() {
		Set<ScanOP> ret = new HashSet<ScanOP>();
		if (this instanceof ScanOP) {
			ret.add(this.asScanOP());
		} else if (this instanceof JoinOP) {
			JoinOP ip = this.asIntersectionOP();
			ret.addAll(ip.getFirstSubQuery().getAllScanOP());
			ret.addAll(ip.getSecondSubQuery().getAllScanOP());
		}
		return ret;
	}

}
